﻿#include <QCoreApplication>
#include <QTimer>
#include <QEventLoop>
#include <QThread>

#include "threadfunction.h"

ThreadRunner::ThreadRunner(bool autoDelete) :
    QObject(),
    m_fun([]() {}),
    m_bAutoDelete(autoDelete)
{

}

ThreadRunner::ThreadRunner(std::function<void ()> fun, bool autoDelete) :
    QObject(),
    m_fun(fun),
    m_bAutoDelete(autoDelete)
{

}

void ThreadRunner::start()
{
    run();
    emit finshed();
    if (m_bAutoDelete)
    {
        QTimer::singleShot(0, this, &ThreadRunner::deleteLater);
    }
}

void ThreadRunner::startThread()
{
    Custom::startThread(this, false);
}

void ThreadRunner::startThreadWithBlock()
{
    Custom::startThread(this, true);
}

void ThreadRunner::run()
{
    m_fun();
}

void Custom::runInMainThread(std::function<void()> fun, bool block)
{
    runInTargetThread(fun, qApp, block);
}

void Custom::runInTargetThread(std::function<void ()> fun, QObject *target, bool block)
{
    if (target->thread() == QThread::currentThread())
    {
        fun();
    }
    else if (block)
    {
        QEventLoop loop;
        QTimer timer;
        timer.setInterval(0);
        timer.setSingleShot(true);
        QObject::connect(&timer, &QTimer::timeout, target, [fun, &loop]()
        {
            QObject obj;
            QObject::connect(&obj, &QObject::destroyed, &loop, &QEventLoop::quit, Qt::QueuedConnection);
            fun();
        }, Qt::QueuedConnection);
        timer.start();
        loop.exec();
    }
    else
    {
        QEventLoop loop;
        QTimer *timer = new QTimer();
        timer->setInterval(0);
        timer->setSingleShot(true);
        QObject::connect(timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        QObject::connect(timer, &QTimer::timeout, target, [fun, timer]()
        {
            QObject obj;
            QObject::connect(&obj, &QObject::destroyed, timer, &QTimer::deleteLater, Qt::QueuedConnection);
            fun();
        }, Qt::QueuedConnection);
        timer->start();
        loop.exec();
    }
}

void Custom::startThread(std::function<void ()> fun, bool block)
{
    startThread(new ThreadRunner(fun, true), block);
}

void Custom::startThread(ThreadRunner *runner, bool block)
{
    QThread *thread = new QThread();
    QThread *oldThread = runner->thread();
    runner->moveToThread(thread);
    QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater, Qt::QueuedConnection);
    QObject::connect(thread, &QThread::started, runner, &ThreadRunner::start, Qt::QueuedConnection);
    QObject::connect(runner, &ThreadRunner::finshed, thread, &QThread::quit, Qt::QueuedConnection);
    QObject::connect(runner, &ThreadRunner::finshed, runner, [runner, oldThread]() { runner->moveToThread(oldThread); } , Qt::QueuedConnection);

    if (block)
    {
        QEventLoop loop;
        QObject::connect(thread, &QThread::finished, &loop, &QEventLoop::quit, Qt::QueuedConnection);
        thread->start();
        loop.exec();
    }
    else
    {
        thread->start();
    }
}
